package ci.demo;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Random;

import com.alibaba.druid.stat.DruidStatService;
import com.alibaba.druid.support.http.util.IPAddress;
import com.alibaba.druid.support.http.util.IPRange;
import com.alibaba.druid.util.Utils;

import ci.web.annotaction.Router;
import ci.web.core.CiContext;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.handler.codec.base64.Base64;
import io.netty.handler.codec.http.Cookie;
import io.netty.handler.codec.http.DefaultCookie;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;

/**
 * druid-serverlet-simple
 * @see com.alibaba.druid.support.http.ResourceServlet
 * @see https://github.com/alibaba/druid/tree/master/src/main/java/com/alibaba/druid/support/http
 * @author Administrator
 *
 */
@Router("druid")
public class DruidStat {
    private static InternalLogger logger = InternalLoggerFactory.getInstance(DruidStat.class);
    
    public static final String SESSION_USER_KEY    = "druid-user";
    public static final String PARAM_NAME_USERNAME = "loginUsername";
    public static final String PARAM_NAME_PASSWORD = "loginPassword";
    public static final String PARAM_NAME_ALLOW    = "allow";
    public static final String PARAM_NAME_DENY     = "deny";
    public static final String PARAM_REMOTE_ADDR   = "remoteAddress";
    
    
    private DruidStatService      statService             = DruidStatService.getInstance();
    private String resoucePath = "support/http/resources";

    protected String           username            = null;
    protected String           password            = null;
    protected List<IPRange>    allowList           = new ArrayList<IPRange>();
    protected List<IPRange>    denyList            = new ArrayList<IPRange>();
    
    private String basePath = "/druid";
    public DruidStat() {
        // 看情况 这里可以  读取配置 也可以写死
//        username = "zhh";
//        password = "pwd";
//        addIpRule("127.0.0.1,192.168.1.100", allowList);
//        addIpRule("192.168.1.102", denyList);
    }
    private void redirect(CiContext ctx, String page){
        ctx.redirect(basePath+'/'+page);
    }
    @Router("*")
    public void api(CiContext ctx) throws IOException{
        if(ctx.path().indexOf('.')<0 && ctx.path().endsWith("/")==false){
            redirect(ctx, "index.html");
            return;
        }
        if (!isPermittedRequest(getRemoteAddress(ctx))) {
            returnResourceFile(ctx, "/nopermit.html");
            return;
        }
        if (isRequireAuth() //
                && !ContainsUser(ctx)//
                ) {
                if(!( ctx.path().endsWith("/login.html") //
                        || ctx.path().endsWith(".js")//
                        || ctx.path().endsWith(".css") //
                   || ctx.path().endsWith(".jpg"))){
                    redirect(ctx, "login.html");
                    return;
                }
        }else if(isRequireAuth() && ctx.path().endsWith("/login.html")){
            redirect(ctx, "index.html");
            return;
        }
        if(ctx.path().endsWith(".json")==false){
            returnResourceFile(ctx);
            return;
        }
        String url = ctx.uri();
        int idx = url.lastIndexOf('/');
        url = url.substring(idx);
//        System.out.println(url);
        String ret = statService.service(url);
        ctx.send(ret);
    }

    //    @Router("/submitLogin")
    public void submitLogin(CiContext ctx){
        String usernameParam = ctx.params().getString(PARAM_NAME_USERNAME);
        String passwordParam = ctx.params().getString(PARAM_NAME_PASSWORD);
        if (username.equals(usernameParam) && password.equals(passwordParam)) {
            putSessionUser(ctx);
            ctx.send("success");
        } else {
            ctx.send("error");
        }
    }

    protected boolean isRequireAuth() {
        return this.username != null;
    }
    private void returnResourceFile(CiContext ctx, String filePath) throws IOException {
        if(filePath.endsWith("/")){
            filePath = filePath+"index.html";
        }
        if (filePath.endsWith(".jpg")) {
            byte[] bytes = Utils.readByteArrayFromResource(filePath);
            if (bytes != null) {
                ctx.send(bytes);
            }
            return;
        }
        String text = Utils.readFromResource(filePath);
        if (text == null) {
            ctx.sendError(404,"Not Found");
            return;
        }
        if (filePath.endsWith(".css")) {
            ctx.setContentType("text/css;charset=utf-8");
        } else if (filePath.endsWith(".js")) {
            ctx.setContentType("text/javascript;charset=utf-8");
        }
        ctx.send(text);   
    }
    private void returnResourceFile(CiContext ctx) throws IOException {
        String filePath = resoucePath+ctx.path().replace(basePath,"");
        returnResourceFile(ctx, filePath);
    }

    private String getRemoteAddress(CiContext ctx) {
        return ((InetSocketAddress) ctx.address()).getAddress().getHostAddress();
    }
    public boolean isPermittedRequest(String remoteAddress) {
        boolean ipV6 = remoteAddress != null && remoteAddress.indexOf(':') != -1;

        if (ipV6) {
            return "0:0:0:0:0:0:0:1".equals(remoteAddress) || (denyList.size() == 0 && allowList.size() == 0);
        }

        IPAddress ipAddress = new IPAddress(remoteAddress);

        for (IPRange range : denyList) {
            if (range.isIPAddressInRange(ipAddress)) {
                return false;
            }
        }

        if (allowList.size() > 0) {
            for (IPRange range : allowList) {
                if (range.isIPAddressInRange(ipAddress)) {
                    return true;
                }
            }

            return false;
        }

        return true;
    }
    
    
    private void putSessionUser(CiContext ctx) {
        String val = randStr(3)+":"+(new Date().getTime())+":"+randStr(3);
        val = xcode(val);
        
        ByteBuf src = Unpooled.wrappedBuffer(val.getBytes());
        ByteBuf dest = Base64.encode(src);
        byte[] ret = new byte[dest.readableBytes()];
        dest.readBytes(ret);
        src.release();
        dest.release();
        val = new String(ret);
        
        Cookie cookie = new DefaultCookie(SESSION_USER_KEY, val);
        cookie.setHttpOnly(true);
        ctx.setCookie(cookie);        
    }
    protected boolean ContainsUser(CiContext ctx) {
        String val = ctx.getCookieValue(SESSION_USER_KEY);
        if(val==null||val.length()<5){
            return false;
        }
        
        ByteBuf src = Unpooled.wrappedBuffer(val.getBytes());
        ByteBuf dest = Base64.decode(src);
        byte[] ret = new byte[dest.readableBytes()];
        dest.readBytes(ret);
        src.release();
        dest.release();
        val = new String(ret);
        
        val = xcode(val);
        int begin = val.indexOf(':');
        int end = val.lastIndexOf(':');
        if(begin<0 || end<0 || begin>=end){
            return false;
        }
        val = val.substring(begin+1, end);
        try{
            long t = Long.parseLong(val);
            long e = new Date().getTime();
            long tt = e-t;
//            System.out.println(tt);
//            System.out.println(7200*1000);
            long exptime = 7200*1000;
            if(tt>0 && tt<exptime){
                if(tt<(exptime/2)){
                    putSessionUser(ctx);
                }
                return true;
            }
        }catch(NumberFormatException e){
        }
        return false;
    }
    
    
    
    
    
    
    protected static void addIpRule(String param, List<IPRange> to){
        try {
            if (param != null && param.trim().length() != 0) {
                param = param.trim();
                String[] items = param.split(",");

                for (String item : items) {
                    if (item == null || item.length() == 0) {
                        continue;
                    }

                    IPRange ipRange = new IPRange(item);
                    to.add(ipRange);
                }
            }
        } catch (Exception e) {
            logger.error(e);
        }
    }
    private static String randStr(int len) {
        String base = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
        Random random = new Random();
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < len; i++) {
            int number = random.nextInt(base.length());
            sb.append(base.charAt(number));
        }
        return sb.toString();
    }
    protected static String xcode(String s){
        return xcode(s,"~ml@pik!or~*)-+w+@gb>");
    }
    protected static String xcode(String s,String key){
        if(key==null||key.length()==0||s==null||s.length()==0)return s;
        int ls = s.length();
        key = fixKey(key, ls);
        StringBuilder sb = new StringBuilder();
        for(int i=0;i<ls;i++){
            char sc = s.charAt(i);
            char sk = key.charAt(i);
            char r = (char) (sc^sk);
            sb.append(r);
        }
        return sb.toString();
    }
    private static String fixKey(String k,int l)
    {
        StringBuilder sb = new StringBuilder(k);
        int num = (int) Math.ceil(l/k.length());
        for(int i=0;i<num;i++){
            sb.append(k);
        }
        sb.setLength(l);
        return sb.toString();
    }
}
